{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "import torch\n",
    "from torchvision import transforms\n",
    "import cv2\n",
    "import math\n",
    "\n",
    "\n",
    "class VGG(nn.Module):\n",
    "    def __init__(self, features, num_classes=1000, init_weights=True):\n",
    "        super(VGG, self).__init__()  #python3中等效于super().__init__()\n",
    "        self.features = features  #前面的卷积结构各层函数，在下面的函数forward()被调用\n",
    "        self.classifier = nn.Sequential(  #从全连接到分类的结构层函数的一个顺序容器\n",
    "            nn.Linear(512 * 7 * 7, 4096),  #第一个全连接层，输入为[batch_size,512*7*7],输出大小为[batch_size,4096],bias默认为True\n",
    "            nn.ReLU(True),  #ReLU激活函数\n",
    "            nn.Dropout(),  #Dropout函数\n",
    "\n",
    "            nn.Linear(4096, 4096),\n",
    "            nn.ReLU(True),\n",
    "            nn.Dropout(),\n",
    "            #最后一层不需要添加激活函数\n",
    "            nn.Linear(4096, num_classes),  #最后一层全连接，输出为num_size，类别的个数\n",
    "        )  #通过Squential将网络层和激活函数和Dropout函数结合起来，输出激活后的网络节点。\n",
    "        if init_weights:\n",
    "            self._initialize_weights()\n",
    "\n",
    "    #备注：nn.Linear(in_features,out_feartures,bias=True)用于设置网络中的全连接层的，需要注意的是全连接层的输入与输出都是二维张量，一般形状为[batch_size, size]\n",
    "    #参数：in_features,out_feartures,bias\n",
    "    #in_features指的是输入的二维张量的大小，即输入的[batch_size, size]中的size。\n",
    "    #out_features指的是输出的二维张量的大小，即输出的二维张量的形状为[batch_size，output_size]，当然，它也代表了该全连接层的神经元个数。\n",
    "\n",
    "    def forward(self, x):  #x为输入的图片张量\n",
    "        x = self.features(x)  #卷积层顺序容器，输入x,输出经过所有卷积层后的特征层\n",
    "        print(x, '-->', type(x), '-->', x.shape, '-->', x.dtype)  #x=[batch_size,C,H,W]\n",
    "        x = x.view(x.size(0), -1)  #把x维度进行调整,保持batch数一致，[batch_size,一个展开后的特征层]\n",
    "        x = self.classifier(x)  #全连接层的顺序容器，最后输出num_classes,用于最后的类别判断\n",
    "        return x\n",
    "\n",
    "    def _initialize_weights(self):  #权重初始化\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, nn.Conv2d):\n",
    "                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n",
    "                m.weight.data.normal_(0, math.sqrt(2. / n))\n",
    "                if m.bias is not None:\n",
    "                    m.bias.data.zero_()\n",
    "            elif isinstance(m, nn.BatchNorm2d):\n",
    "                m.weight.data.fill_(1)\n",
    "                m.bias.data.zero_()\n",
    "            elif isinstance(m, nn.Linear):\n",
    "                m.weight.data.normal_(0, 0.01)\n",
    "                m.bias.data.zero_()\n",
    "\n",
    "\n",
    "def make_layers(cfg, batch_norm=False):  #卷积层的实现函数\n",
    "    layers = []\n",
    "    in_channels = 3  #初始通道数为3，RGB\n",
    "    for v in cfg:\n",
    "        if v == 'M':  #判断是否为池化层\n",
    "            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]  #经过最大池化，W，H变为原来的1/2,\n",
    "        else:  #卷积层+/BN+ReLU\n",
    "            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)  #卷积层(输入通道数,输出通道数,卷积核的大小,边缘补数)\n",
    "            if batch_norm:  #批处理规范化\n",
    "                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]\n",
    "            else:  #不进行批处理\n",
    "                layers += [conv2d, nn.ReLU(inplace=True)]\n",
    "            in_channels = v  #更改通道数\n",
    "    return nn.Sequential(*layers)  #返回卷积部分的顺序容器\n",
    "\n",
    "\n",
    "cfg = {\n",
    "    'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],\n",
    "    'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],\n",
    "    'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],\n",
    "    'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],\n",
    "}  #网络的卷积层池化层结构类型\n",
    "\n",
    "\n",
    "def vgg11(**kwargs):\n",
    "    model = VGG(make_layers(cfg['A']), **kwargs)  #实例化类VGG,此处执行类的构造函数__init__\n",
    "    #这里把VGG结构的第一个结构vgg11的卷积层部分，make_layers(cfg['A'])作为feature传入VGG类中\n",
    "    return model  #返回卷积层部分的顺序容器和全连接层部分的顺序容器\n",
    "\n",
    "\n",
    "def vgg11_bn(**kwargs):  #是在vgg11上添加batch_noorm\n",
    "    model = VGG(make_layers(cfg['A'], batch_norm=True), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg13(**kwargs):\n",
    "    model = VGG(make_layers(cfg['B']), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg13_bn(**kwargs):\n",
    "    model = VGG(make_layers(cfg['B'], batch_norm=True), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg16(**kwargs):\n",
    "    model = VGG(make_layers(cfg['D']), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg16_bn(**kwargs):\n",
    "    model = VGG(make_layers(cfg['D'], batch_norm=True), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg19(**kwargs):\n",
    "    model = VGG(make_layers(cfg['E']), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "def vgg19_bn(**kwargs):\n",
    "    model = VGG(make_layers(cfg['E'], batch_norm=True), **kwargs)\n",
    "    return model\n",
    "\n",
    "\n",
    "img = cv2.imread(\"img/1.jpg\")  # 读取输入网络的图片\n",
    "print(img, '-->', type(img), '-->', img.shape, '-->', img.dtype)\n",
    "trans = transforms.Compose(\n",
    "    [\n",
    "        transforms.ToTensor(),  #Convert a PIL Image or numpy.ndarray to tensor.\n",
    "        #transforms.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))\n",
    "    ])\n",
    "img = trans(img)  #把读入的图片数据转化为torch.tensor类型的数据,[C,H,W]\n",
    "print(img, '-->', type(img), '-->', img.shape)\n",
    "img = img.unsqueeze(0)  # 图片扩展多一维,因为输入到保存的模型中是4维的[batch_size,通道,长，宽]，而普通图片只有三维，[通道,长，宽]\n",
    "print(img, '-->', type(img), '-->', img.shape, '-->', img.dtype)\n",
    "\n",
    "if __name__ == '__main__':  #执行本文件时，if条件满足，执行下面的语句\n",
    "\n",
    "    net19 = vgg19()  #调用vgg19，vgg19()实例化类VGG,执行类的构造方法，返回卷积层和全连接层的顺序容器\n",
    "    print(net19)\n",
    "    x = net19.forward(img)  #调用类的函数forward()，把img送入函数，执行真个网络，得到1000类的输出\n",
    "    print(x, '-->', type(x), '-->', x.shape, '-->', x.dtype)\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}